home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / mail.c < prev    next >
C/C++ Source or Header  |  1996-10-15  |  55KB  |  1,901 lines

  1. /*
  2.  * Program:    Mailbox Access routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 November 1989
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <time.h>
  42. #include "misc.h"
  43. #include "rfc822.h"
  44.  
  45.  
  46. /* c-client global data */
  47.  
  48.                 /* list of mail drivers */
  49. static DRIVER *maildrivers = NIL;
  50.                 /* pointer to alternate gets function */
  51. static mailgets_t mailgets = NIL;
  52.                 /* mail cache manipulation function */
  53. static mailcache_t mailcache = mm_cache;
  54.                 /* place to stash last user name */
  55. static char *mailusernamebuf = NIL;
  56.                 /* pointer to alternate rfc822 generator */
  57. static rfc822emit_t rfc822_emit = rfc822_output;
  58.                 /* pointer to SMTP verbose output collector */
  59. static postverbose_t post_verbose = NIL;
  60.                 /* POST send string as record */
  61. static postsoutr_t post_soutr = tcp_soutr;
  62.                 /* POST receive line */
  63. static postgetline_t post_getline = tcp_getline;
  64.                 /* POST close connection */
  65. static postclose_t post_close = tcp_close;
  66.  
  67. /* Default limited get string
  68.  * Accepts: readin function pointer
  69.  *        stream to use
  70.  *        number of bytes
  71.  * Returns: string read in, truncated if necessary
  72.  *
  73.  * This is a sample mailgets routine.  It simply truncates any data larger
  74.  * than MAXMESSAGESIZE.  On most systems, you generally don't use a mailgets
  75.  * routine at all, but on some systems (e.g. DOS) it's required to prevent the
  76.  * application from crashing.  This one is filled in by the os driver for any
  77.  * OS that requires a mailgets routine and the main program has not already
  78.  * supplied one, generally in tcp_open().
  79.  */
  80.  
  81. char *mm_gets (f,stream,size)
  82.     readfn_t f;
  83.     void *stream;
  84.     unsigned long size;
  85. {
  86.   char *s;
  87.   char tmp[MAILTMPLEN+1];
  88.   unsigned long i,j = 0;
  89.                 /* truncate? */
  90.   if (i = (size > MAXMESSAGESIZE)) {
  91.     sprintf (tmp,"%ld character literal truncated to %ld characters",
  92.          size,MAXMESSAGESIZE);
  93.     mm_log (tmp,WARN);        /* warn user */
  94.     i = size - MAXMESSAGESIZE;    /* number of bytes of slop */
  95.     size = MAXMESSAGESIZE;    /* maximum length string we can read */
  96.   }
  97.   s = (char *) fs_get (size + 1);
  98.   *s = s[size] = '\0';        /* init in case getbuffer fails */
  99.   (*f) (stream,size,s);        /* get the literal */
  100.                 /* toss out everything after that */
  101.   while (i -= j) (*f) (stream,j = min ((long) MAILTMPLEN,i),tmp);
  102.   return s;
  103. }
  104.  
  105. /* Default mail cache handler
  106.  * Accepts: pointer to cache handle
  107.  *        message number
  108.  *        caching function
  109.  * Returns: cache data
  110.  */
  111.  
  112. void *mm_cache (stream,msgno,op)
  113.     MAILSTREAM *stream;
  114.     long msgno;
  115.     long op;
  116. {
  117.   size_t new;
  118.   void *ret = NIL;
  119.   long i = msgno - 1;
  120.   unsigned long j = stream->cachesize;
  121.   switch ((int) op) {        /* what function? */
  122.   case CH_INIT:            /* initialize cache */
  123.     if (stream->cachesize) {    /* flush old cache contents */
  124.       while (stream->cachesize) mm_cache (stream,stream->cachesize--,CH_FREE);
  125.       fs_give ((void **) &stream->cache.c);
  126.       stream->nmsgs = 0;    /* can't have any messages now */
  127.     }
  128.     break;
  129.   case CH_SIZE:            /* (re-)size the cache */
  130.     if (msgno > j) {        /* do nothing if size adequate */
  131.       new = (stream->cachesize = msgno + CACHEINCREMENT) * sizeof (void *);
  132.       if (stream->cache.c) fs_resize ((void **) &stream->cache.c,new);
  133.       else stream->cache.c = (void **) fs_get (new);
  134.                 /* init cache */
  135.       while (j < stream->cachesize) stream->cache.c[j++] = NIL;
  136.     }
  137.     break;
  138.   case CH_MAKELELT:        /* return long elt, make if necessary */
  139.     if (!stream->cache.c[i]) {    /* have one already? */
  140.                 /* no, instantiate it */
  141.       stream->cache.l[i] = (LONGCACHE *) fs_get (sizeof (LONGCACHE));
  142.       memset (&stream->cache.l[i]->elt,0,sizeof (MESSAGECACHE));
  143.       stream->cache.l[i]->elt.lockcount = 1;
  144.       stream->cache.l[i]->elt.msgno = msgno;
  145.       stream->cache.l[i]->env = NIL;
  146.       stream->cache.l[i]->body = NIL;
  147.     }
  148.                 /* drop in to CH_LELT */
  149.   case CH_LELT:            /* return long elt */
  150.     ret = stream->cache.c[i];    /* return void version */
  151.     break;
  152.  
  153.   case CH_MAKEELT:        /* return short elt, make if necessary */
  154.     if (!stream->cache.c[i]) {    /* have one already? */
  155.       if (stream->scache) {    /* short cache? */
  156.     stream->cache.s[i] = (MESSAGECACHE *) fs_get (sizeof(MESSAGECACHE));
  157.     memset (stream->cache.s[i],0,sizeof (MESSAGECACHE));
  158.     stream->cache.s[i]->lockcount = 1;
  159.     stream->cache.s[i]->msgno = msgno;
  160.       }
  161.       else mm_cache (stream,msgno,CH_MAKELELT);
  162.     }
  163.                 /* drop in to CH_ELT */
  164.   case CH_ELT:            /* return short elt */
  165.     ret = stream->cache.c[i] && !stream->scache ?
  166.       (void *) &stream->cache.l[i]->elt : stream->cache.c[i];
  167.     break;
  168.   case CH_FREE:            /* free (l)elt */
  169.     if (stream->scache) mail_free_elt (&stream->cache.s[i]);
  170.     else mail_free_lelt (&stream->cache.l[i]);
  171.     break;
  172.   case CH_EXPUNGE:        /* expunge cache slot */
  173.                 /* slide down remainder of cache */
  174.     for (i = msgno; i < stream->nmsgs; ++i)
  175.       if (stream->cache.c[i-1] = stream->cache.c[i])
  176.     ((MESSAGECACHE *) mm_cache (stream,i,CH_ELT))->msgno = i;
  177.     stream->cache.c[stream->nmsgs-1] = NIL;
  178.     break;
  179.   default:
  180.     fatal ("Bad mm_cache op");
  181.     break;
  182.   }
  183.   return ret;
  184. }
  185.  
  186. /* Dummy string driver for complete in-memory strings */
  187.  
  188. STRINGDRIVER mail_string = {
  189.   mail_string_init,        /* initialize string structure */
  190.   mail_string_next,        /* get next byte in string structure */
  191.   mail_string_setpos        /* set position in string structure */
  192. };
  193.  
  194.  
  195. /* Initialize mail string structure for in-memory string
  196.  * Accepts: string structure
  197.  *        pointer to string
  198.  *        size of string
  199.  */
  200.  
  201. void mail_string_init (s,data,size)
  202.     STRING *s;
  203.     void *data;
  204.     unsigned long size;
  205. {
  206.                 /* set initial string pointers */
  207.   s->chunk = s->curpos = (char *) (s->data = data);
  208.                 /* and sizes */
  209.   s->size = s->chunksize = s->cursize = size;
  210.   s->data1 = s->offset = 0;    /* never any offset */
  211. }
  212.  
  213. /* Get next character from string
  214.  * Accepts: string structure
  215.  * Returns: character, string structure chunk refreshed
  216.  */
  217.  
  218. char mail_string_next (s)
  219.     STRING *s;
  220. {
  221.   return *s->curpos++;        /* return the last byte */
  222. }
  223.  
  224.  
  225. /* Set string pointer position
  226.  * Accepts: string structure
  227.  *        new position
  228.  */
  229.  
  230. void mail_string_setpos (s,i)
  231.     STRING *s;
  232.     unsigned long i;
  233. {
  234.   s->curpos = s->chunk + i;    /* set new position */
  235.   s->cursize = s->chunksize - i;/* and new size */
  236. }
  237.  
  238. /* Mail routines
  239.  *
  240.  *  mail_xxx routines are the interface between this module and the outside
  241.  * world.  Only these routines should be referenced by external callers.
  242.  *
  243.  *  Note that there is an important difference between a "sequence" and a
  244.  * "message #" (msgno).  A sequence is a string representing a sequence in
  245.  * {"n", "n:m", or combination separated by commas} format, whereas a msgno
  246.  * is a single integer.
  247.  *
  248.  */
  249.  
  250. /* Mail link driver
  251.  * Accepts: driver to add to list
  252.  */
  253.  
  254. void mail_link (driver)
  255.     DRIVER *driver;
  256. {
  257.   DRIVER **d = &maildrivers;
  258.   while (*d) d = &(*d)->next;    /* find end of list of drivers */
  259.   *d = driver;            /* put driver at the end */
  260.   driver->next = NIL;        /* this driver is the end of the list */
  261. }
  262.  
  263. /* Mail manipulate driver parameters
  264.  * Accepts: mail stream
  265.  *        function code
  266.  *        function-dependent value
  267.  * Returns: function-dependent return value
  268.  */
  269.  
  270. void *mail_parameters (stream,function,value)
  271.     MAILSTREAM *stream;
  272.     long function;
  273.     void *value;
  274. {
  275.   void *r,*ret = NIL;
  276.   DRIVER *d;
  277.   switch ((int) function) {
  278.   case SET_DRIVERS:
  279.     fatal ("SET_DRIVERS not permitted");
  280.   case GET_DRIVERS:
  281.     ret = (void *) maildrivers;
  282.     break;
  283.   case SET_GETS:
  284.     mailgets = (mailgets_t) value;
  285.   case GET_GETS:
  286.     ret = (void *) mailgets;
  287.     break;
  288.   case SET_CACHE:
  289.     mailcache = (mailcache_t) value;
  290.   case GET_CACHE:
  291.     ret = (void *) mailcache;
  292.     break;
  293.   case SET_USERNAMEBUF:
  294.     mailusernamebuf = (char *) value;
  295.     /* BUG: missing break?  anyone care? */
  296.   case GET_USERNAMEBUF:
  297.     ret = (void *) mailusernamebuf;
  298.     break;
  299.   case SET_RFC822OUTPUT:
  300.     rfc822_emit = (rfc822emit_t) value;
  301.     break;
  302.   case GET_RFC822OUTPUT:
  303.     ret = (void *) rfc822_emit;
  304.     break;
  305.   case SET_POSTVERBOSE:
  306.     post_verbose = (postverbose_t) value;
  307.     break;
  308.   case GET_POSTVERBOSE:
  309.     ret = (void *) post_verbose;
  310.     break;
  311.   case SET_POSTSOUTR:
  312.     post_soutr = (postsoutr_t) value;
  313.     break;
  314.   case GET_POSTSOUTR:
  315.     ret = (void *) post_soutr;
  316.     break;
  317.   case SET_POSTGETLINE:
  318.     post_getline = (postgetline_t) value;
  319.     break;
  320.   case GET_POSTGETLINE:
  321.     ret = (void *) post_getline;
  322.     break;
  323.   case SET_POSTCLOSE:
  324.     post_close = (postclose_t) value;
  325.     break;
  326.   case GET_POSTCLOSE:
  327.     ret = (void *) post_close;
  328.     break;
  329.   default:
  330.     if (stream && stream->dtb)    /* if have stream, do for that stream only */
  331.       ret = (*stream->dtb->parameters) (function,value);
  332.                 /* else do all drivers */
  333.     else for (d = maildrivers; d; d = d->next)
  334.       if (r = (d->parameters) (function,value)) ret = r;
  335.                 /* do environment */
  336.     if (r = env_parameters (function,value)) ret = r;
  337.                 /* do TCP/IP */
  338.     if (r = tcp_parameters (function,value)) ret = r;
  339.     break;
  340.   }
  341.   return ret;
  342. }
  343.  
  344. /* Mail find list of subscribed mailboxes
  345.  * Accepts: mail stream
  346.  *        pattern to search
  347.  */
  348.  
  349. void mail_find (stream,pat)
  350.     MAILSTREAM *stream;
  351.     char *pat;
  352. {
  353.   DRIVER *d = maildrivers;
  354.                 /* if have a stream, do it for that stream */
  355.   if (stream && stream->dtb) (*stream->dtb->find) (stream,pat);
  356.   else do (d->find) (NIL,pat);    /* otherwise do for all DTB's */
  357.   while (d = d->next);        /* until at the end */
  358. }
  359.  
  360.  
  361. /* Mail find list of subscribed bboards
  362.  * Accepts: mail stream
  363.  *        pattern to search
  364.  */
  365.  
  366. void mail_find_bboards (stream,pat)
  367.     MAILSTREAM *stream;
  368.     char *pat;
  369. {
  370.   DRIVER *d = maildrivers;
  371.   if (stream && stream->dtb) (*stream->dtb->find_bboard) (stream,pat);
  372.   else do (d->find_bboard) (NIL,pat);
  373.   while (d = d->next);        /* until at the end */
  374. }
  375.  
  376. /* Mail find list of all mailboxes
  377.  * Accepts: mail stream
  378.  *        pattern to search
  379.  */
  380.  
  381. void mail_find_all (stream,pat)
  382.     MAILSTREAM *stream;
  383.     char *pat;
  384. {
  385.   DRIVER *d = maildrivers;
  386.                 /* if have a stream, do it for that stream */
  387.   if (stream && stream->dtb) (*stream->dtb->find_all) (stream,pat);
  388.                 /* otherwise do for all DTB's */
  389.   else do (d->find_all) (NIL,pat);
  390.   while (d = d->next);        /* until at the end */
  391. }
  392.  
  393.  
  394. /* Mail find list of all bboards
  395.  * Accepts: mail stream
  396.  *        pattern to search
  397.  */
  398.  
  399. void mail_find_all_bboard (stream,pat)
  400.     MAILSTREAM *stream;
  401.     char *pat;
  402. {
  403.   DRIVER *d = maildrivers;
  404.                 /* if have a stream, do it for that stream */
  405.   if (stream && stream->dtb) (*stream->dtb->find_all_bboard) (stream,pat);
  406.                 /* otherwise do for all DTB's */
  407.   else do (d->find_all_bboard) (NIL,pat);
  408.   while (d = d->next);        /* until at the end */
  409. }
  410.  
  411. /* Mail validate mailbox name
  412.  * Accepts: MAIL stream
  413.  *        mailbox name
  414.  *        purpose string for error message
  415.  * Return: driver factory on success, NIL on failure
  416.  */
  417.  
  418. DRIVER *mail_valid (stream,mailbox,purpose)
  419.     MAILSTREAM *stream;
  420.     char *mailbox;
  421.     char *purpose;
  422. {
  423.   char *s,tmp[MAILTMPLEN];
  424.   DRIVER *factory;
  425.   for (factory = maildrivers; factory && !(*factory->valid) (mailbox);
  426.        factory = factory->next);
  427.                 /* must match stream if not dummy */
  428.   if (factory && stream && (stream->dtb != factory))
  429.     factory = strcmp (factory->name,"dummy") ? NIL : stream->dtb;
  430.   if (!factory && purpose) {    /* if want an error message */
  431.     switch (*mailbox) {        /* error depends upon first character */
  432.     case '*':            /* bboard */
  433.       if (mailbox[1] != '{') {    /* but only if local */
  434.     s = "no such bboard";
  435.     break;
  436.       }
  437.                 /* otherwise drop into remote */
  438.     case '{':            /* remote specification */
  439.       s = "invalid remote specification";
  440.       break;
  441.     default:            /* others */
  442.       s = "no such mailbox";
  443.       break;
  444.     }
  445.     sprintf (tmp,"Can't %s %s: %s",purpose,mailbox,s);
  446.     mm_log (tmp,ERROR);
  447.   }
  448.   return factory;        /* return driver factory */
  449. }
  450.  
  451. /* Mail validate network mailbox name
  452.  * Accepts: mailbox name
  453.  *        mailbox driver to validate against
  454.  *        pointer to where to return host name if non-NIL
  455.  *        pointer to where to return mailbox name if non-NIL
  456.  * Returns: driver on success, NIL on failure
  457.  */
  458.  
  459. DRIVER *mail_valid_net (name,drv,host,mailbox)
  460.     char *name;
  461.     DRIVER *drv;
  462.     char *host;
  463.     char *mailbox;
  464. {
  465.   NETMBX mb;
  466.   if (!mail_valid_net_parse (name,&mb) ||
  467.       (strcmp (mb.service,"imap") ? strcmp (mb.service,drv->name) :
  468.        strncmp (drv->name,"imap",4))) return NIL;
  469.   if (host) strcpy (host,mb.host);
  470.   if (mailbox) strcpy (mailbox,mb.mailbox);
  471.   return drv;
  472. }
  473.  
  474.  
  475. /* Mail validate network mailbox name
  476.  * Accepts: mailbox name
  477.  *        NETMBX structure to return values
  478.  * Returns: T on success, NIL on failure
  479.  */
  480.  
  481. long mail_valid_net_parse (name,mb)
  482.     char *name;
  483.     NETMBX *mb;
  484. {
  485.   long i;
  486.   char c,*s,*t,*v;
  487.   mb->port = 0;            /* initialize structure */
  488.   *mb->host = *mb->mailbox = *mb->service = '\0';
  489.                 /* init flags */
  490.   mb->anoflag = mb->dbgflag = NIL;
  491.   if (mailusernamebuf) *mailusernamebuf = '\0';
  492.                 /* check if bboard */
  493.   if (mb->bbdflag = (*name == '*') ? T : NIL) name++;
  494.                 /* have host specification? */
  495.   if (!(*name == '{' && (t = strchr (s = name+1,'}')) && (i = t - s)))
  496.     return NIL;            /* not valid host specification */
  497.   strncpy (mb->host,s,i);    /* set host name */
  498.   mb->host[i] = '\0';        /* tie it off */
  499.   strcpy (mb->mailbox,t+1);    /* set mailbox name */
  500.                 /* any switches or port specification? */
  501.   if (t = strpbrk (mb->host,"/:")) {
  502.     c = *t;            /* yes, remember delimiter */
  503.     *t++ = '\0';        /* tie off host name */
  504.     lcase (t);            /* coerce remaining stuff to lowercase */
  505.     do switch (c) {        /* act based upon the character */
  506.     case ':':            /* port specification */
  507.       if (mb->port || ((mb->port = strtol (t,&t,10)) <= 0)) return NIL;
  508.       c = t ? *t++ : '\0';    /* get delimiter, advance pointer */
  509.       break;
  510.  
  511.     case '/':            /* switch */
  512.                 /* find delimiter */
  513.       if (t = strpbrk (s = t,"/:=")) {
  514.     c = *t;            /* remember delimiter for later */
  515.     *t++ = '\0';        /* tie off switch name */
  516.       }
  517.       else c = '\0';        /* no delimiter */
  518.       if (c == '=') {        /* parse switches which take arguments */
  519.     if (t = strpbrk (v = t,"/:")) {
  520.       c = *t;        /* remember delimiter for later */
  521.       *t++ = '\0';        /* tie off switch name */
  522.     }
  523.     else c = '\0';        /* no delimiter */
  524.     if (!strcmp (s,"service")) {
  525.       if (*mb->service) return NIL;
  526.       else strcpy (mb->service,v);
  527.     }
  528.     if (!strcmp (s,"user") && mailusernamebuf) {
  529.       if (*mailusernamebuf) return NIL;
  530.       else strcpy (mailusernamebuf,v);
  531.     }
  532.     else return NIL;    /* invalid argument switch */
  533.       }
  534.       else {            /* non-argument switch */
  535.     if (!strcmp (s,"anonymous")) mb->anoflag = T;
  536.     else if (!strcmp (s,"bboard")) mb->bbdflag = T;
  537.     else if (!strcmp (s,"debug")) mb->dbgflag = T;
  538.     else if (!strcmp (s,"imap") || !strcmp (s,"imap2") ||
  539.          !strcmp (s,"imap4") || !strcmp (s,"pop3") ||
  540.          !strcmp (s,"nntp")) {
  541.       if (*mb->service) return NIL;
  542.       else strcpy (mb->service,s);
  543.     }
  544.     else return NIL;    /* invalid non-argument switch */
  545.       }
  546.       break;
  547.     default:            /* anything else is bogus */
  548.       return NIL;
  549.     } while (c);        /* see if anything more to parse */
  550.   }
  551.                 /* default mailbox name */
  552.   if (!*mb->mailbox) strcpy (mb->mailbox,mb->bbdflag ? "general" : "INBOX");
  553.                 /* default service name */
  554.   if (!*mb->service) strcpy (mb->service,"imap");
  555.   return T;
  556. }
  557.  
  558. /* Mail subscribe to mailbox
  559.  * Accepts: mail stream
  560.  *        mailbox to add to subscription list
  561.  * Returns: T on success, NIL on failure
  562.  */
  563.  
  564. long mail_subscribe (stream,mailbox)
  565.     MAILSTREAM *stream;
  566.     char *mailbox;
  567. {
  568.   DRIVER *factory = mail_valid (stream,mailbox,"subscribe to mailbox");
  569.   return factory ? (*factory->subscribe) (stream,mailbox) : NIL;
  570. }
  571.  
  572.  
  573. /* Mail unsubscribe to mailbox
  574.  * Accepts: mail stream
  575.  *        mailbox to delete from subscription list
  576.  * Returns: T on success, NIL on failure
  577.  */
  578.  
  579. long mail_unsubscribe (stream,mailbox)
  580.     MAILSTREAM *stream;
  581.     char *mailbox;
  582. {
  583.  DRIVER *factory = mail_valid (stream,mailbox,"unsubscribe to mailbox");
  584.   return factory ? (*factory->unsubscribe) (stream,mailbox) : NIL;
  585. }
  586.  
  587.  
  588. /* Mail subscribe to bboard
  589.  * Accepts: mail stream
  590.  *        bboard to add to subscription list
  591.  * Returns: T on success, NIL on failure
  592.  */
  593.  
  594. long mail_subscribe_bboard (stream,mailbox)
  595.     MAILSTREAM *stream;
  596.     char *mailbox;
  597. {
  598.   char tmp[MAILTMPLEN];
  599.   DRIVER *factory;
  600.   sprintf (tmp,"*%s",mailbox);
  601.   return (factory = mail_valid (stream,tmp,"subscribe to bboard")) ?
  602.     (*factory->subscribe_bboard) (stream,mailbox) : NIL;
  603. }
  604.  
  605.  
  606. /* Mail unsubscribe to bboard
  607.  * Accepts: mail stream
  608.  *        bboard to delete from subscription list
  609.  * Returns: T on success, NIL on failure
  610.  */
  611.  
  612. long mail_unsubscribe_bboard (stream,mailbox)
  613.     MAILSTREAM *stream;
  614.     char *mailbox;
  615. {
  616.   char tmp[MAILTMPLEN];
  617.   DRIVER *factory;
  618.   sprintf (tmp,"*%s",mailbox);
  619.   return (factory = mail_valid (stream,tmp,"unsubscribe to bboard")) ?
  620.     (*factory->unsubscribe_bboard) (stream,mailbox) : NIL;
  621. }
  622.  
  623. /* Mail create mailbox
  624.  * Accepts: mail stream
  625.  *        mailbox name to create
  626.  * Returns: T on success, NIL on failure
  627.  */
  628.  
  629. long mail_create (stream,mailbox)
  630.     MAILSTREAM *stream;
  631.     char *mailbox;
  632. {
  633.   /* A local mailbox is one that is not qualified as being a remote or a
  634.    * namespace mailbox.  Any remote or namespace mailbox driver must check
  635.    * for itself whether or not the mailbox already exists. */
  636.   int localp = (*mailbox != '{') && (*mailbox != '#');
  637.   char tmp[MAILTMPLEN];
  638.                 /* guess at driver if stream not specified */
  639.   if (!(stream || (stream = localp ? default_proto () :
  640.            mail_open (NIL,mailbox,OP_PROTOTYPE)))) {
  641.     sprintf (tmp,"Can't create mailbox %s: indeterminate format",mailbox);
  642.     mm_log (tmp,ERROR);
  643.     return NIL;
  644.   }
  645.                 /* must not already exist if local */
  646.   if (localp && mail_valid (stream,mailbox,NIL)) {
  647.     sprintf (tmp,"Can't create mailbox %s: mailbox already exists",mailbox);
  648.     mm_log (tmp,ERROR);
  649.     return NIL;
  650.   }
  651.   return stream->dtb ? (*stream->dtb->create) (stream,mailbox) : NIL;
  652. }
  653.  
  654. /* Mail delete mailbox
  655.  * Accepts: mail stream
  656.  *        mailbox name to delete
  657.  * Returns: T on success, NIL on failure
  658.  */
  659.  
  660. long mail_delete (stream,mailbox)
  661.     MAILSTREAM *stream;
  662.     char *mailbox;
  663. {
  664.   DRIVER *factory = mail_valid (stream,mailbox,"delete mailbox");
  665.   return factory ? (*factory->mbxdel) (stream,mailbox) : NIL;
  666. }
  667.  
  668.  
  669. /* Mail rename mailbox
  670.  * Accepts: mail stream
  671.  *        old mailbox name
  672.  *        new mailbox name
  673.  * Returns: T on success, NIL on failure
  674.  */
  675.  
  676. long mail_rename (stream,old,new)
  677.     MAILSTREAM *stream;
  678.     char *old;
  679.     char *new;
  680. {
  681.   char tmp[MAILTMPLEN];
  682.   DRIVER *factory = mail_valid (stream,old,"rename mailbox");
  683.   if ((*old != '{') && (*old != '#') && mail_valid (NIL,new,NIL)) {
  684.     sprintf (tmp,"Can't rename to mailbox %s: mailbox already exists",new);
  685.     mm_log (tmp,ERROR);
  686.     return NIL;
  687.   }
  688.   return factory ? (*factory->mbxren) (stream,old,new) : NIL;
  689. }
  690.  
  691. /* Mail open
  692.  * Accepts: candidate stream for recycling
  693.  *        mailbox name
  694.  *        open options
  695.  * Returns: stream to use on success, NIL on failure
  696.  */
  697.  
  698. MAILSTREAM *mail_open (stream,name,options)
  699.     MAILSTREAM *stream;
  700.     char *name;
  701.     long options;
  702. {
  703.   DRIVER *factory = mail_valid (NIL,name,(options & OP_SILENT) ?
  704.                 NIL : "open mailbox");
  705.   if (factory) {        /* must have a factory */
  706.     if (!stream) {        /* instantiate stream if none to recycle */
  707.       if (options & OP_PROTOTYPE) return (*factory->open) (NIL);
  708.       stream = (MAILSTREAM *) fs_get (sizeof (MAILSTREAM));
  709.                 /* initialize stream */
  710.       memset ((void *) stream,0,sizeof (MAILSTREAM));
  711.       stream->dtb = factory;    /* set dispatch */
  712.                 /* set mailbox name */
  713.       stream->mailbox = cpystr (name);
  714.                 /* initialize cache */
  715.       (*mailcache) (stream,(long) 0,CH_INIT);
  716.     }
  717.     else {            /* close driver if different from factory */
  718.       if (stream->dtb != factory) {
  719.     if (stream->dtb) (*stream->dtb->close) (stream);
  720.     stream->dtb = factory;    /* establish factory as our driver */
  721.     stream->local = NIL;    /* flush old driver's local data */
  722.     mail_free_cache (stream);
  723.       }
  724.                 /* clean up old mailbox name for recycling */
  725.       if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  726.       stream->mailbox = cpystr (name);
  727.     }
  728.     stream->lock = NIL;        /* initialize lock and options */
  729.     stream->debug = (options & OP_DEBUG) ? T : NIL;
  730.     stream->rdonly = (options & OP_READONLY) ? T : NIL;
  731.     stream->anonymous = (options & OP_ANONYMOUS) ? T : NIL;
  732.     stream->scache = (options & OP_SHORTCACHE) ? T : NIL;
  733.     stream->silent = (options & OP_SILENT) ? T : NIL;
  734.     stream->halfopen = (options & OP_HALFOPEN) ? T : NIL;
  735.                 /* have driver open, flush if failed */
  736.     if (!(*factory->open) (stream)) stream = mail_close (stream);
  737.   }
  738.   return stream;        /* return the stream */
  739. }
  740.  
  741. /* Mail close
  742.  * Accepts: mail stream
  743.  * Returns: NIL
  744.  */
  745.  
  746. MAILSTREAM *mail_close (stream)
  747.     MAILSTREAM *stream;
  748. {
  749.   if (stream) {            /* make sure argument given */
  750.                 /* do the driver's close action */
  751.     if (stream->dtb) (*stream->dtb->close) (stream);
  752.     if (stream->mailbox) fs_give ((void **) &stream->mailbox);
  753.     stream->sequence++;        /* invalidate sequence */
  754.     if (stream->flagstring) fs_give ((void **) &stream->flagstring);
  755.     mail_free_cache (stream);    /* finally free the stream's storage */
  756.     if (!stream->use) fs_give ((void **) &stream);
  757.   }
  758.   return NIL;
  759. }
  760.  
  761. /* Mail make handle
  762.  * Accepts: mail stream
  763.  * Returns: handle
  764.  *
  765.  *  Handles provide a way to have multiple pointers to a stream yet allow the
  766.  * stream's owner to nuke it or recycle it.
  767.  */
  768.  
  769. MAILHANDLE *mail_makehandle (stream)
  770.     MAILSTREAM *stream;
  771. {
  772.   MAILHANDLE *handle = (MAILHANDLE *) fs_get (sizeof (MAILHANDLE));
  773.   handle->stream = stream;    /* copy stream */
  774.                 /* and its sequence */
  775.   handle->sequence = stream->sequence;
  776.   stream->use++;        /* let stream know another handle exists */
  777.   return handle;
  778. }
  779.  
  780.  
  781. /* Mail release handle
  782.  * Accepts: Mail handle
  783.  */
  784.  
  785. void mail_free_handle (handle)
  786.     MAILHANDLE **handle;
  787. {
  788.   MAILSTREAM *s;
  789.   if (*handle) {        /* only free if exists */
  790.                 /* resign stream, flush unreferenced zombies */
  791.     if ((!--(s = (*handle)->stream)->use) && !s->dtb) fs_give ((void **) &s);
  792.     fs_give ((void **) handle);    /* now flush the handle */
  793.   }
  794. }
  795.  
  796.  
  797. /* Mail get stream handle
  798.  * Accepts: Mail handle
  799.  * Returns: mail stream or NIL if stream gone
  800.  */
  801.  
  802. MAILSTREAM *mail_stream (handle)
  803.     MAILHANDLE *handle;
  804. {
  805.   MAILSTREAM *s = handle->stream;
  806.   return (s->dtb && (handle->sequence == s->sequence)) ? s : NIL;
  807. }
  808.  
  809. /* Mail fetch long cache element
  810.  * Accepts: mail stream
  811.  *        message # to fetch
  812.  * Returns: long cache element of this message
  813.  * Can also be used to create cache elements for new messages.
  814.  */
  815.  
  816. LONGCACHE *mail_lelt (stream,msgno)
  817.     MAILSTREAM *stream;
  818.     long msgno;
  819. {
  820.   if (stream->scache) fatal ("Short cache in mail_lelt");
  821.                 /* be sure it the cache is large enough */
  822.   (*mailcache) (stream,msgno,CH_SIZE);
  823.   return (LONGCACHE *) (*mailcache) (stream,msgno,CH_MAKELELT);
  824. }
  825.  
  826.  
  827. /* Mail fetch cache element
  828.  * Accepts: mail stream
  829.  *        message # to fetch
  830.  * Returns: cache element of this message
  831.  * Can also be used to create cache elements for new messages.
  832.  */
  833.  
  834. MESSAGECACHE *mail_elt (stream,msgno)
  835.     MAILSTREAM *stream;
  836.     long msgno;
  837. {
  838.   if (msgno < 1) fatal ("Bad msgno in mail_elt");
  839.                 /* be sure it the cache is large enough */
  840.   (*mailcache) (stream,msgno,CH_SIZE);
  841.   return (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_MAKEELT);
  842. }
  843.  
  844. /* Mail fetch fast information
  845.  * Accepts: mail stream
  846.  *        sequence
  847.  *
  848.  * Generally, mail_fetchstructure is preferred
  849.  */
  850.  
  851. void mail_fetchfast (stream,sequence)
  852.     MAILSTREAM *stream;
  853.     char *sequence;
  854. {
  855.                   /* do the driver's action */
  856.   if (stream->dtb) (*stream->dtb->fetchfast) (stream,sequence);
  857. }
  858.  
  859.  
  860. /* Mail fetch flags
  861.  * Accepts: mail stream
  862.  *        sequence
  863.  */
  864.  
  865. void mail_fetchflags (stream,sequence)
  866.     MAILSTREAM *stream;
  867.     char *sequence;
  868. {
  869.                   /* do the driver's action */
  870.   if (stream->dtb) (*stream->dtb->fetchflags) (stream,sequence);
  871. }
  872.  
  873.  
  874. /* Mail fetch message structure
  875.  * Accepts: mail stream
  876.  *        message # to fetch
  877.  *        pointer to return body
  878.  * Returns: envelope of this message, body returned in body value
  879.  *
  880.  * Fetches the "fast" information as well
  881.  */
  882.  
  883. ENVELOPE *mail_fetchstructure (stream,msgno,body)
  884.     MAILSTREAM *stream;
  885.     long msgno;
  886.     BODY **body;
  887. {
  888.   if (msgno < 1 || msgno > stream->nmsgs)
  889.     fatal ("Bad msgno in mail_fetchstructure");
  890.                   /* do the driver's action */
  891.   return stream->dtb ? (*stream->dtb->fetchstructure) (stream,msgno,body) :NIL;
  892. }
  893.  
  894. /* Mail fetch message header
  895.  * Accepts: mail stream
  896.  *        message # to fetch
  897.  * Returns: message header in RFC822 format
  898.  */
  899.  
  900. char *mail_fetchheader (stream,msgno)
  901.     MAILSTREAM *stream;
  902.     long msgno;
  903. {
  904.   if (msgno < 1 || msgno > stream->nmsgs)
  905.     fatal ("Bad msgno in mail_fetchheader");
  906.                   /* do the driver's action */
  907.   return stream->dtb ? (*stream->dtb->fetchheader) (stream,msgno) : "";
  908. }
  909.  
  910.  
  911. /* Mail fetch message text (only)
  912.     body only;
  913.  * Accepts: mail stream
  914.  *        message # to fetch
  915.  * Returns: message text in RFC822 format
  916.  */
  917.  
  918. char *mail_fetchtext (stream,msgno)
  919.     MAILSTREAM *stream;
  920.     long msgno;
  921. {
  922.   if (msgno < 1 || msgno > stream->nmsgs)
  923.     fatal ("Bad msgno in mail_fetchtext");
  924.                   /* do the driver's action */
  925.   return stream->dtb ? (*stream->dtb->fetchtext) (stream,msgno) : "";
  926. }
  927.  
  928.  
  929. /* Mail fetch message body part text
  930.  * Accepts: mail stream
  931.  *        message # to fetch
  932.  *        section specifier (#.#.#...#)
  933.  *        pointer to returned length
  934.  * Returns: pointer to section of message body
  935.  */
  936.  
  937. char *mail_fetchbody (stream,m,sec,len)
  938.     MAILSTREAM *stream;
  939.     long m;
  940.     char *sec;
  941.     unsigned long *len;
  942. {
  943.   if (m < 1 || m > stream->nmsgs) fatal ("Bad msgno in mail_fetchbody");
  944.                   /* do the driver's action */
  945.   return stream->dtb ? (*stream->dtb->fetchbody) (stream,m,sec,len) : "";
  946. }
  947.  
  948. /* Mail fetch From string for menu
  949.  * Accepts: destination string
  950.  *        mail stream
  951.  *        message # to fetch
  952.  *        desired string length
  953.  * Returns: string of requested length
  954.  */
  955.  
  956. void mail_fetchfrom (s,stream,msgno,length)
  957.     char *s;
  958.     MAILSTREAM *stream;
  959.     long msgno;
  960.     long length;
  961. {
  962.   char *t;
  963.   char tmp[MAILTMPLEN];
  964.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  965.   ADDRESS *adr = env ? env->from : NIL;
  966.   memset (s,' ',length);    /* fill it with spaces */
  967.   s[length] = '\0';        /* tie off with null */
  968.                 /* get first from address from envelope */
  969.   while (adr && !adr->host) adr = adr->next;
  970.   if (adr) {            /* if a personal name exists use it */
  971.     if (!(t = adr->personal)) sprintf (t = tmp,"%s@%s",adr->mailbox,adr->host);
  972.     memcpy (s,t,min (length,(long) strlen (t)));
  973.   }
  974. }
  975.  
  976.  
  977. /* Mail fetch Subject string for menu
  978.  * Accepts: destination string
  979.  *        mail stream
  980.  *        message # to fetch
  981.  *        desired string length
  982.  * Returns: string of no more than requested length
  983.  */
  984.  
  985. void mail_fetchsubject (s,stream,msgno,length)
  986.     char *s;
  987.     MAILSTREAM *stream;
  988.     long msgno;
  989.     long length;
  990. {
  991.   ENVELOPE *env = mail_fetchstructure (stream,msgno,NIL);
  992.   memset (s,'\0',length+1);
  993.                 /* copy subject from envelope */
  994.   if (env && env->subject) strncpy (s,env->subject,length);
  995.   else *s = ' ';        /* if no subject then just a space */
  996. }
  997.  
  998. /* Mail set flag
  999.  * Accepts: mail stream
  1000.  *        sequence
  1001.  *        flag(s)
  1002.  */
  1003.  
  1004. void mail_setflag (stream,sequence,flag)
  1005.     MAILSTREAM *stream;
  1006.     char *sequence;
  1007.     char *flag;
  1008. {
  1009.                   /* do the driver's action */
  1010.   if (stream->dtb) (*stream->dtb->setflag) (stream,sequence,flag);
  1011. }
  1012.  
  1013.  
  1014. /* Mail clear flag
  1015.  * Accepts: mail stream
  1016.  *        sequence
  1017.  *        flag(s)
  1018.  */
  1019.  
  1020. void mail_clearflag (stream,sequence,flag)
  1021.     MAILSTREAM *stream;
  1022.     char *sequence;
  1023.     char *flag;
  1024. {
  1025.                   /* do the driver's action */
  1026.   if (stream->dtb) (*stream->dtb->clearflag) (stream,sequence,flag);
  1027. }
  1028.  
  1029.  
  1030. /* Mail search for messages
  1031.  * Accepts: mail stream
  1032.  *        search criteria
  1033.  */
  1034.  
  1035. void mail_search (stream,criteria)
  1036.     MAILSTREAM *stream;
  1037.     char *criteria;
  1038. {
  1039.   long i = 1;
  1040.   while (i <= stream->nmsgs) mail_elt (stream,i++)->searched = NIL;
  1041.                   /* do the driver's action */
  1042.   if (stream->dtb) (*stream->dtb->search) (stream,criteria);
  1043. }
  1044.  
  1045.  
  1046. /* Mail ping mailbox
  1047.  * Accepts: mail stream
  1048.  * Returns: stream if still open else NIL
  1049.  */
  1050.  
  1051. long mail_ping (stream)
  1052.     MAILSTREAM *stream;
  1053. {
  1054.                   /* do the driver's action */
  1055.   return stream->dtb ? (*stream->dtb->ping) (stream) : NIL;
  1056. }
  1057.  
  1058. /* Mail check mailbox
  1059.  * Accepts: mail stream
  1060.  */
  1061.  
  1062. void mail_check (stream)
  1063.     MAILSTREAM *stream;
  1064. {
  1065.                   /* do the driver's action */
  1066.   if (stream->dtb) (*stream->dtb->check) (stream);
  1067. }
  1068.  
  1069.  
  1070. /* Mail expunge mailbox
  1071.  * Accepts: mail stream
  1072.  */
  1073.  
  1074. void mail_expunge (stream)
  1075.     MAILSTREAM *stream;
  1076. {
  1077.                   /* do the driver's action */
  1078.   if (stream->dtb) (*stream->dtb->expunge) (stream);
  1079. }
  1080.  
  1081. /* Mail copy message(s)
  1082.     s;
  1083.  * Accepts: mail stream
  1084.  *        sequence
  1085.  *        destination mailbox
  1086.  */
  1087.  
  1088. long mail_copy (stream,sequence,mailbox)
  1089.     MAILSTREAM *stream;
  1090.     char *sequence;
  1091.     char *mailbox;
  1092. {
  1093.                   /* do the driver's action */
  1094.   return stream->dtb ? (*stream->dtb->copy) (stream,sequence,mailbox) : NIL;
  1095. }
  1096.  
  1097.  
  1098. /* Mail move message(s)
  1099.     s;
  1100.  * Accepts: mail stream
  1101.  *        sequence
  1102.  *        destination mailbox
  1103.  */
  1104.  
  1105. long mail_move (stream,sequence,mailbox)
  1106.     MAILSTREAM *stream;
  1107.     char *sequence;
  1108.     char *mailbox;
  1109. {
  1110.                   /* do the driver's action */
  1111.   return stream->dtb ? (*stream->dtb->move) (stream,sequence,mailbox) : NIL;
  1112. }
  1113.  
  1114.  
  1115. /* Mail append message string
  1116.  * Accepts: mail stream
  1117.  *        destination mailbox
  1118.  *        initial flags
  1119.  *        message internal date
  1120.  *        stringstruct of message to append
  1121.  * Returns: T on success, NIL on failure
  1122.  */
  1123.  
  1124. long mail_append (stream,mailbox,message)
  1125.     MAILSTREAM *stream;
  1126.     char *mailbox;
  1127.     STRING *message;
  1128. {
  1129.                 /* compatibility jacket */
  1130.   return mail_append_full (stream,mailbox,NIL,NIL,message);
  1131. }
  1132.  
  1133.  
  1134. long mail_append_full (stream,mailbox,flags,date,message)
  1135.     MAILSTREAM *stream;
  1136.     char *mailbox;
  1137.     char *flags;
  1138.     char *date;
  1139.                     STRING *message;
  1140. {
  1141.   DRIVER *factory = mail_valid (stream,mailbox,NIL);
  1142.   if (!factory) {        /* got a driver to use? */
  1143.     if (!stream &&        /* ask default for TRYCREATE if no stream */
  1144.     (*default_proto ()->dtb->append) (stream,mailbox,flags,date,message)) {
  1145.                 /* timing race?? */
  1146.       mm_notify (stream,"Append validity confusion",WARN);
  1147.       return T;
  1148.     }
  1149.                 /* now generate error message */
  1150.     mail_valid (stream,mailbox,"append to mailbox");
  1151.     return NIL;            /* return failure */
  1152.   }
  1153.                 /* do the driver's action */
  1154.   return (factory->append) (stream,mailbox,flags,date,message);
  1155. }
  1156.  
  1157. /* Mail garbage collect stream
  1158.  * Accepts: mail stream
  1159.  *        garbage collection flags
  1160.  */
  1161.  
  1162. void mail_gc (stream,gcflags)
  1163.     MAILSTREAM *stream;
  1164.     long gcflags;
  1165. {
  1166.   unsigned long i = 1;
  1167.   LONGCACHE *lelt;
  1168.                   /* do the driver's action first */
  1169.   if (stream->dtb) (*stream->dtb->gc) (stream,gcflags);
  1170.   if (gcflags & GC_ENV) {    /* garbage collect envelopes? */
  1171.                 /* yes, free long cache if in use */
  1172.     if (!stream->scache) while (i <= stream->nmsgs)
  1173.       if (lelt = (LONGCACHE *) (*mailcache) (stream,i++,CH_LELT)) {
  1174.     mail_free_envelope (&lelt->env);
  1175.     mail_free_body (&lelt->body);
  1176.       }
  1177.     stream->msgno = 0;        /* free this cruft too */
  1178.     mail_free_envelope (&stream->env);
  1179.     mail_free_body (&stream->body);
  1180.   }
  1181.                 /* free text if any */
  1182.   if ((gcflags & GC_TEXTS) && (stream->text)) fs_give ((void **)&stream->text);
  1183. }
  1184.  
  1185. /* Mail output date from elt fields
  1186.  * Accepts: character string to write into
  1187.  *        elt to get data data from
  1188.  * Returns: the character string
  1189.  */
  1190.  
  1191. const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  1192.  
  1193. const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1194.             "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1195.  
  1196. char *mail_date (string,elt)
  1197.     char *string;
  1198.     MESSAGECACHE *elt;
  1199. {
  1200.   const char *s = (elt->month && elt->month < 13) ?
  1201.     months[elt->month - 1] : (const char *) "???";
  1202.   sprintf (string,"%2d-%s-%d %02d:%02d:%02d %c%02d%02d",
  1203.        elt->day,s,elt->year + BASEYEAR,
  1204.        elt->hours,elt->minutes,elt->seconds,
  1205.        elt->zoccident ? '-' : '+',elt->zhours,elt->zminutes);
  1206.   return string;
  1207. }
  1208.  
  1209.  
  1210. /* Mail output cdate format date from elt fields
  1211.  * Accepts: character string to write into
  1212.  *        elt to get data data from
  1213.  * Returns: the character string
  1214.  */
  1215.  
  1216. char *mail_cdate (string,elt)
  1217.     char *string;
  1218.     MESSAGECACHE *elt;
  1219. {
  1220.   const char *s = (elt->month && elt->month < 13) ?
  1221.     months[elt->month - 1] : (const char *) "???";
  1222.   int m = elt->month;
  1223.   int y = elt->year + BASEYEAR;
  1224.   if (elt->month <= 2) {    /* if before March, */
  1225.     m = elt->month + 9;        /* January = month 10 of previous year */
  1226.     y--;
  1227.   }
  1228.   else m = elt->month - 3;    /* March is month 0 */
  1229.   sprintf (string,"%s %s %2d %02d:%02d:%02d %4d\n",
  1230.        days[(int)(elt->day+2+((7+31*m)/12)+y+(y/4)+(y/400)-(y/100)) % 7],s,
  1231.        elt->day,elt->hours,elt->minutes,elt->seconds,elt->year + BASEYEAR);
  1232.   return string;
  1233. }
  1234.  
  1235. /* Mail parse date into elt fields
  1236.  * Accepts: elt to write into
  1237.  *        date string to parse
  1238.  * Returns: T if parse successful, else NIL
  1239.  * This routine parses dates as follows:
  1240.  * . leading three alphas followed by comma and space are ignored
  1241.  * . date accepted in format: mm/dd/yy, mm/dd/yyyy, dd-mmm-yy, dd-mmm-yyyy,
  1242.  *    dd mmm yy, dd mmm yyyy
  1243.  * . space or end of string required
  1244.  * . time accepted in format hh:mm:ss or hh:mm
  1245.  * . end of string accepted
  1246.  * . timezone accepted: hyphen followed by symbolic timezone, or space
  1247.  *    followed by signed numeric timezone or symbolic timezone
  1248.  * Examples of normal input:
  1249.  * . IMAP date-only (SEARCH): dd-mmm-yy, dd-mmm-yyyy, mm/dd/yy, mm/dd/yyyy
  1250.  * . IMAP date-time (INTERNALDATE):
  1251.  *    dd-mmm-yy hh:mm:ss-zzz
  1252.  *    dd-mmm-yyyy hh:mm:ss +zzzz
  1253.  * . RFC-822:
  1254.  *    www, dd mmm yy hh:mm:ss zzz
  1255.  *    www, dd mmm yyyy hh:mm:ss +zzzz
  1256.  */
  1257.  
  1258. long mail_parse_date (elt,s)
  1259.     MESSAGECACHE *elt;
  1260.     char *s;
  1261. {
  1262.   long d,m,y;
  1263.   int ms;
  1264.   struct tm *t;
  1265.   time_t tn;
  1266.   char tmp[MAILTMPLEN];
  1267.                 /* make a writeable uppercase copy */
  1268.   if (s && *s && (strlen (s) < MAILTMPLEN)) s = ucase (strcpy (tmp,s));
  1269.   else return NIL;
  1270.                 /* skip over possible day of week */
  1271.   if (isalpha (*s) && isalpha (s[1]) && isalpha (s[2]) && (s[3] == ',') &&
  1272.       (s[4] == ' ')) s += 5;
  1273.   while (*s == ' ') s++;    /* parse first number (probable month) */
  1274.   if (!(m = strtol ((const char *) s,&s,10))) return NIL;
  1275.  
  1276.   switch (*s) {            /* different parse based on delimiter */
  1277.   case '/':            /* mm/dd/yy format */
  1278.     if (!((d = strtol ((const char *) ++s,&s,10)) && *s == '/' &&
  1279.       (y = strtol ((const char *) ++s,&s,10)) && *s == '\0')) return NIL;
  1280.     break;
  1281.   case ' ':            /* dd mmm yy format */
  1282.     while (s[1] == ' ') s++;    /* slurp extra whitespace */
  1283.   case '-':            /* dd-mmm-yy format */
  1284.     d = m;            /* so the number we got is a day */
  1285.                 /* make sure string long enough! */
  1286.     if (strlen (s) < 5) return NIL;
  1287.     /* Some compilers don't allow `<<' and/or longs in case statements. */
  1288.                 /* slurp up the month string */
  1289.     ms = ((s[1] - 'A') * 1024) + ((s[2] - 'A') * 32) + (s[3] - 'A');
  1290.     switch (ms) {        /* determine the month */
  1291.     case (('J'-'A') * 1024) + (('A'-'A') * 32) + ('N'-'A'): m = 1; break;
  1292.     case (('F'-'A') * 1024) + (('E'-'A') * 32) + ('B'-'A'): m = 2; break;
  1293.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('R'-'A'): m = 3; break;
  1294.     case (('A'-'A') * 1024) + (('P'-'A') * 32) + ('R'-'A'): m = 4; break;
  1295.     case (('M'-'A') * 1024) + (('A'-'A') * 32) + ('Y'-'A'): m = 5; break;
  1296.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('N'-'A'): m = 6; break;
  1297.     case (('J'-'A') * 1024) + (('U'-'A') * 32) + ('L'-'A'): m = 7; break;
  1298.     case (('A'-'A') * 1024) + (('U'-'A') * 32) + ('G'-'A'): m = 8; break;
  1299.     case (('S'-'A') * 1024) + (('E'-'A') * 32) + ('P'-'A'): m = 9; break;
  1300.     case (('O'-'A') * 1024) + (('C'-'A') * 32) + ('T'-'A'): m = 10; break;
  1301.     case (('N'-'A') * 1024) + (('O'-'A') * 32) + ('V'-'A'): m = 11; break;
  1302.     case (('D'-'A') * 1024) + (('E'-'A') * 32) + ('C'-'A'): m = 12; break;
  1303.     default: return NIL;    /* unknown month */
  1304.     }
  1305.     if ((s[4] == *s) &&    (y = strtol ((const char *) s+5,&s,10)) &&
  1306.     (*s == '\0' || *s == ' ')) break;
  1307.   default: return NIL;        /* unknown date format */
  1308.   }
  1309.                 /* minimal validity check of date */
  1310.   if (d < 1 || d > 31 || m < 1 || m > 12 || y < 0) return NIL;
  1311.                 /* Tenex/ARPAnet began in 1969 */
  1312.   if (y < 100) y += (y >= (BASEYEAR - 1900)) ? 1900 : 2000;
  1313.                 /* set values in elt */
  1314.   elt->day = d; elt->month = m; elt->year = y - BASEYEAR;
  1315.  
  1316.   if (*s) {            /* time specification present? */
  1317.                 /* parse time */
  1318.     d = strtol ((const char *) s,&s,10);
  1319.     if (*s != ':') return NIL;
  1320.     m = strtol ((const char *) ++s,&s,10);
  1321.     y = (*s == ':') ? strtol ((const char *) ++s,&s,10) : 0;
  1322.                 /* validity check time */
  1323.     if (d < 0 || d > 23 || m < 0 || m > 59 || y < 0 || y > 59) return NIL;
  1324.                 /* set values in elt */
  1325.     elt->hours = d; elt->minutes = m; elt->seconds = y;
  1326.     switch (*s) {        /* time zone specifier? */
  1327.     case ' ':            /* numeric time zone */
  1328.       while (s[1] == ' ') s++;    /* slurp extra whitespace */
  1329.       if (!isalpha (s[1])) {    /* treat as '-' case if alphabetic */
  1330.                 /* test for sign character */
  1331.     if ((elt->zoccident = (*++s == '-')) || (*s == '+')) s++;
  1332.                 /* validate proper timezone */
  1333.     if (isdigit(*s) && isdigit(s[1]) && isdigit(s[2]) && isdigit(s[3])) {
  1334.       elt->zhours = (*s - '0') * 10 + (s[1] - '0');
  1335.       elt->zminutes = (s[2] - '0') * 10 + (s[3] - '0');
  1336.     }
  1337.     return T;        /* all done! */
  1338.       }
  1339.                 /* falls through */
  1340.  
  1341.     case '-':            /* symbolic time zone */
  1342.       if (!(ms = *++s)) ms = 'Z';
  1343.       else if (*++s) {        /* multi-character? */
  1344.     ms -= 'A'; ms *= 1024;    /* yes, make compressed three-byte form */
  1345.     ms += ((*s++ - 'A') * 32);
  1346.     if (*s) ms += *s++ - 'A';
  1347.     if (*s) ms = '\0';    /* more than three characters */
  1348.       }
  1349.       /* This is not intended to be a comprehensive list of all possible
  1350.        * timezone strings.  Such a list would be impractical.  Rather, this
  1351.        * listing is intended to incorporate all military, north American, and
  1352.        * a few special cases such as Japan and the major European zone names,
  1353.        * such as what might be expected to be found in a Tenex format mailbox
  1354.        * and spewed from an IMAP server.  The trend is to migrate to numeric
  1355.        * timezones which lack the flavor but also the ambiguity of the names.
  1356.        */
  1357.       switch (ms) {        /* determine the timezone */
  1358.     /* oriental (from Greenwich) timezones */
  1359.                 /* Middle Europe */
  1360.       case (('M'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1361.       case 'A': elt->zhours = 1; break;
  1362.                 /* Eastern Europe */
  1363.       case (('E'-'A')*1024)+(('E'-'A')*32)+'T'-'A':
  1364.       case 'B': elt->zhours = 2; break;
  1365.       case 'C': elt->zhours = 3; break;
  1366.       case 'D': elt->zhours = 4; break;
  1367.       case 'E': elt->zhours = 5; break;
  1368.       case 'F': elt->zhours = 6; break;
  1369.       case 'G': elt->zhours = 7; break;
  1370.       case 'H': elt->zhours = 8; break;
  1371.                 /* Japan */
  1372.       case (('J'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1373.       case 'I': elt->zhours = 9; break;
  1374.       case 'K': elt->zhours = 10; break;
  1375.       case 'L': elt->zhours = 11; break;
  1376.       case 'M': elt->zhours = 12; break;
  1377.  
  1378.     /* occidental (from Greenwich) timezones */
  1379.       case 'N': elt->zoccident = 1; elt->zhours = 1; break;
  1380.       case 'O': elt->zoccident = 1; elt->zhours = 2; break;
  1381.       case (('A'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1382.       case 'P': elt->zoccident = 1; elt->zhours = 3; break;
  1383.                 /* Atlantic */
  1384.       case (('A'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1385.       case (('E'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1386.       case 'Q': elt->zoccident = 1; elt->zhours = 4; break;
  1387.                 /* Eastern */
  1388.       case (('E'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1389.       case (('C'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1390.       case 'R': elt->zoccident = 1; elt->zhours = 5; break;
  1391.                 /* Central */
  1392.       case (('C'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1393.       case (('M'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1394.       case 'S': elt->zoccident = 1; elt->zhours = 6; break;
  1395.                 /* Mountain */
  1396.       case (('M'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1397.       case (('P'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1398.       case 'T': elt->zoccident = 1; elt->zhours = 7; break;
  1399.                 /* Pacific */
  1400.       case (('P'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1401.       case (('Y'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1402.       case 'U': elt->zoccident = 1; elt->zhours = 8; break;
  1403.                 /* Yukon */
  1404.       case (('Y'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1405.       case (('H'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1406.       case 'V': elt->zoccident = 1; elt->zhours = 9; break;
  1407.                 /* Hawaii */
  1408.       case (('H'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1409.       case (('B'-'A')*1024)+(('D'-'A')*32)+'T'-'A':
  1410.       case 'W': elt->zoccident = 1; elt->zhours = 10; break;
  1411.                 /* Bering */
  1412.       case (('B'-'A')*1024)+(('S'-'A')*32)+'T'-'A':
  1413.       case 'X': elt->zoccident = 1; elt->zhours = 11; break;
  1414.       case 'Y': elt->zoccident = 1; elt->zhours = 12; break;
  1415.                 /* Universal */
  1416.       case (('U'-'A')*1024)+(('T'-'A')*32):
  1417.       case (('G'-'A')*1024)+(('M'-'A')*32)+'T'-'A':
  1418.       case 'Z': elt->zhours = 0; break;
  1419.  
  1420.       default:            /* assume local otherwise */
  1421.     tn = time (0);        /* time now... */
  1422.     t = localtime (&tn);    /* get local minutes since midnight */
  1423.     m = t->tm_hour * 60 + t->tm_min;
  1424.     ms = t->tm_yday;    /* note Julian day */
  1425.     t = gmtime (&tn);    /* minus UTC minutes since midnight */
  1426.     m -= t->tm_hour * 60 + t->tm_min;
  1427.     /* ms can be one of:
  1428.      *  36x  local time is December 31, UTC is January 1, offset -24 hours
  1429.      *    1  local time is 1 day ahead of UTC, offset +24 hours
  1430.      *    0  local time is same day as UTC, no offset
  1431.      *   -1  local time is 1 day behind UTC, offset -24 hours
  1432.      * -36x  local time is January 1, UTC is December 31, offset +24 hours
  1433.      */
  1434.     if (ms -= t->tm_yday)    /* correct offset if different Julian day */
  1435.       m += ((ms < 0) == (abs (ms) == 1)) ? -24*60 : 24*60;
  1436.     if (m < 0) {        /* occidental? */
  1437.       m = abs (m);        /* yup, make positive number */
  1438.       elt->zoccident = 1;    /* and note west of UTC */
  1439.     }
  1440.     elt->zhours = m / 60;    /* now break into hours and minutes */
  1441.     elt->zminutes = m % 60;
  1442.     break;
  1443.       }
  1444.       elt->zminutes = 0;    /* never a fractional hour */
  1445.       break;
  1446.     case '\0':            /* no time zone */
  1447.     default:            /* bogus time zone */
  1448.       break;            /* ignore both cases */
  1449.     }
  1450.   }
  1451.   else {            /* make sure all time fields zero */
  1452.     elt->hours = elt->minutes = elt->seconds = elt->zhours = elt->zminutes =
  1453.       elt->zoccident = 0;
  1454.   }
  1455.   return T;
  1456. }
  1457.  
  1458. /* Mail messages have been searched out
  1459.  * Accepts: mail stream
  1460.  *        message number
  1461.  *
  1462.  * Calls external "mm_searched" function to notify main program
  1463.  */
  1464.  
  1465. void mail_searched (stream,msgno)
  1466.     MAILSTREAM *stream;
  1467.     long msgno;
  1468. {
  1469.                 /* mark as searched */
  1470.   mail_elt (stream,msgno)->searched = T;
  1471.   mm_searched (stream,msgno);    /* notify main program */
  1472. }
  1473.  
  1474.  
  1475. /* Mail n messages exist
  1476.  * Accepts: mail stream
  1477.  *        number of messages
  1478.  *
  1479.  * Calls external "mm_exists" function that notifies main program prior
  1480.  * to updating the stream
  1481.  */
  1482.  
  1483. void mail_exists (stream,nmsgs)
  1484.     MAILSTREAM *stream;
  1485.     long nmsgs;
  1486. {
  1487.                 /* make sure cache is large enough */
  1488.   (*mailcache) (stream,nmsgs,CH_SIZE);
  1489.                 /* notify main program of change */
  1490.   if (!stream->silent) mm_exists (stream,nmsgs);
  1491.   stream->nmsgs = nmsgs;    /* update stream status */
  1492. }
  1493.  
  1494. /* Mail n messages are recent
  1495.  * Accepts: mail stream
  1496.  *        number of recent messages
  1497.  */
  1498.  
  1499. void mail_recent (stream,recent)
  1500.     MAILSTREAM *stream;
  1501.     long recent;
  1502. {
  1503.   stream->recent = recent;    /* update stream status */
  1504. }
  1505.  
  1506.  
  1507. /* Mail message n is expunged
  1508.  * Accepts: mail stream
  1509.  *        message #
  1510.  *
  1511.  * Calls external "mm_expunged" function that notifies main program prior
  1512.  * to updating the stream
  1513.  */
  1514.  
  1515. void mail_expunged (stream,msgno)
  1516.     MAILSTREAM *stream;
  1517.     long msgno;
  1518. {
  1519.   long i = msgno - 1;
  1520.   MESSAGECACHE *elt = (MESSAGECACHE *) (*mailcache) (stream,msgno,CH_ELT);
  1521.   if (elt) {            /* if an element is there */
  1522.     elt->msgno = 0;        /* invalidate its message number and free */
  1523.     (*mailcache) (stream,msgno,CH_FREE);
  1524.   }
  1525.                 /* expunge the slot */
  1526.   (*mailcache) (stream,msgno,CH_EXPUNGE);
  1527.   --stream->nmsgs;        /* update stream status */
  1528.   stream->msgno = 0;        /* nuke the short cache too */
  1529.   mail_free_envelope (&stream->env);
  1530.   mail_free_body (&stream->body);
  1531.                 /* notify main program of change */
  1532.   if (!stream->silent) mm_expunged (stream,msgno);
  1533. }
  1534.  
  1535. /* mail stream status routines */
  1536.  
  1537.  
  1538. /* Mail lock stream
  1539.  * Accepts: mail stream
  1540.  */
  1541.  
  1542. void mail_lock (stream)
  1543.     MAILSTREAM *stream;
  1544. {
  1545.   if (stream->lock) fatal ("Lock when already locked");
  1546.   else stream->lock = T;    /* lock stream */
  1547. }
  1548.  
  1549.  
  1550. /* Mail unlock stream
  1551.  * Accepts: mail stream
  1552.  */
  1553.  
  1554. void mail_unlock (stream)
  1555.     MAILSTREAM *stream;
  1556. {
  1557.   if (!stream->lock) fatal ("Unlock when not locked");
  1558.   else stream->lock = NIL;    /* unlock stream */
  1559. }
  1560.  
  1561.  
  1562. /* Mail turn on debugging telemetry
  1563.  * Accepts: mail stream
  1564.  */
  1565.  
  1566. void mail_debug (stream)
  1567.     MAILSTREAM *stream;
  1568. {
  1569.   stream->debug = T;        /* turn on debugging telemetry */
  1570. }
  1571.  
  1572.  
  1573. /* Mail turn off debugging telemetry
  1574.  * Accepts: mail stream
  1575.  */
  1576.  
  1577. void mail_nodebug (stream)
  1578.     MAILSTREAM *stream;
  1579. {
  1580.   stream->debug = NIL;        /* turn off debugging telemetry */
  1581. }
  1582.  
  1583. /* Mail parse sequence
  1584.  * Accepts: mail stream
  1585.  *        sequence to parse
  1586.  * Returns: T if parse successful, else NIL
  1587.  */
  1588.  
  1589. long mail_sequence (stream,sequence)
  1590.     MAILSTREAM *stream;
  1591.     char *sequence;
  1592. {
  1593.   long i,j,x;
  1594.   for (i = 1; i <= stream->nmsgs; i++) mail_elt (stream,i)->sequence = NIL;
  1595.   while (*sequence) {        /* while there is something to parse */
  1596.                 /* parse and validate message number */
  1597.     if (((i = (int) strtol ((const char *) sequence,&sequence,10)) < 1) ||
  1598.     (i > stream->nmsgs)) {
  1599.       mm_log ("Sequence invalid",ERROR);
  1600.       return NIL;
  1601.     }
  1602.     switch (*sequence) {    /* see what the delimiter is */
  1603.     case ':':            /* sequence range */
  1604.                 /* parse end of range */
  1605.       if (((j = (int) strtol ((const char *) ++sequence,&sequence,10)) < 1) ||
  1606.       (j > stream->nmsgs) || (*sequence && *sequence++ != ',')) {
  1607.     mm_log ("Sequence range invalid",ERROR);
  1608.     return NIL;
  1609.       }
  1610.       if (i > j) {        /* swap the range if backwards */
  1611.     x = i; i = j; j = x;
  1612.       }
  1613.                 /* mark each item in the sequence */
  1614.       while (i <= j) mail_elt (stream,j--)->sequence = T;
  1615.       break;
  1616.     case ',':            /* single message */
  1617.       ++sequence;        /* skip the delimiter, fall into end case */
  1618.     case '\0':            /* end of sequence, mark this message */
  1619.       mail_elt (stream,i)->sequence = T;
  1620.       break;
  1621.     default:            /* anything else is a syntax error! */
  1622.       mm_log ("Syntax error in sequence",ERROR);
  1623.       return NIL;
  1624.     }
  1625.   }
  1626.   return T;            /* successfully parsed sequence */
  1627. }
  1628.  
  1629. /* Mail data structure instantiation routines */
  1630.  
  1631.  
  1632. /* Mail instantiate envelope
  1633.  * Returns: new envelope
  1634.  */
  1635.  
  1636. ENVELOPE *mail_newenvelope ()
  1637. {
  1638.   ENVELOPE *env = (ENVELOPE *) fs_get (sizeof (ENVELOPE));
  1639.   env->remail = NIL;        /* initialize all fields */
  1640.   env->return_path = NIL;
  1641.   env->date = NIL;
  1642.   env->subject = NIL;
  1643.   env->from = env->sender = env->reply_to = env->to = env->cc = env->bcc = NIL;
  1644.   env->in_reply_to = env->message_id = env->newsgroups = env->followup_to =
  1645.     env->references = NIL;
  1646.   return env;
  1647. }
  1648.  
  1649.  
  1650. /* Mail instantiate address
  1651.  * Returns: new address
  1652.  */
  1653.  
  1654. ADDRESS *mail_newaddr ()
  1655. {
  1656.   ADDRESS *adr = (ADDRESS *) fs_get (sizeof (ADDRESS));
  1657.                 /* initialize all fields */
  1658.   adr->personal = adr->adl = adr->mailbox = adr->host = adr->error = NIL;
  1659.   adr->next = NIL;
  1660.   return adr;
  1661. }
  1662.  
  1663.  
  1664. /* Mail instantiate body
  1665.  * Returns: new body
  1666.  */
  1667.  
  1668. BODY *mail_newbody ()
  1669. {
  1670.   return mail_initbody ((BODY *) fs_get (sizeof (BODY)));
  1671. }
  1672.  
  1673. /* Mail initialize body
  1674.  * Accepts: body
  1675.  * Returns: body
  1676.  */
  1677.  
  1678. BODY *mail_initbody (body)
  1679.     BODY *body;
  1680. {
  1681.   body->type = TYPETEXT;    /* content type */
  1682.   body->encoding = ENC7BIT;    /* content encoding */
  1683.   body->subtype = body->id = body->description = NIL;
  1684.   body->parameter = NIL;
  1685.   body->contents.text = NIL;    /* no contents yet */
  1686.   body->contents.binary = NIL;
  1687.   body->contents.part = NIL;
  1688.   body->contents.msg.env = NIL;
  1689.   body->contents.msg.body = NIL;
  1690.   body->contents.msg.text = NIL;
  1691.   body->size.lines = body->size.bytes = body->size.ibytes = 0;
  1692.   body->md5 = NIL;
  1693.   return body;
  1694. }
  1695.  
  1696.  
  1697. /* Mail instantiate body parameter
  1698.  * Returns: new body part
  1699.  */
  1700.  
  1701. PARAMETER *mail_newbody_parameter ()
  1702. {
  1703.   PARAMETER *parameter = (PARAMETER *) fs_get (sizeof (PARAMETER));
  1704.   parameter->attribute = parameter->value = NIL;
  1705.   parameter->next = NIL;    /* no next yet */
  1706.   return parameter;
  1707. }
  1708.  
  1709.  
  1710. /* Mail instantiate body part
  1711.  * Returns: new body part
  1712.  */
  1713.  
  1714. PART *mail_newbody_part ()
  1715. {
  1716.   PART *part = (PART *) fs_get (sizeof (PART));
  1717.   mail_initbody (&part->body);    /* initialize the body */
  1718.   part->offset = 0;        /* no offset yet */
  1719.   part->next = NIL;        /* no next yet */
  1720.   return part;
  1721. }
  1722.  
  1723. /* Mail garbage collection routines */
  1724.  
  1725.  
  1726. /* Mail garbage collect body
  1727.  * Accepts: pointer to body pointer
  1728.  */
  1729.  
  1730. void mail_free_body (body)
  1731.     BODY **body;
  1732. {
  1733.   if (*body) {            /* only free if exists */
  1734.     mail_free_body_data (*body);/* free its data */
  1735.     fs_give ((void **) body);    /* return body to free storage */
  1736.   }
  1737. }
  1738.  
  1739.  
  1740. /* Mail garbage collect body data
  1741.  * Accepts: body pointer
  1742.  */
  1743.  
  1744. void mail_free_body_data (body)
  1745.     BODY *body;
  1746. {
  1747.   if (body->subtype) fs_give ((void **) &body->subtype);
  1748.   mail_free_body_parameter (&body->parameter);
  1749.   if (body->id) fs_give ((void **) &body->id);
  1750.   if (body->description) fs_give ((void **) &body->description);
  1751.   if (body->md5) fs_give ((void **) &body->md5);
  1752.   switch (body->type) {        /* free contents */
  1753.   case TYPETEXT:        /* unformatted text */
  1754.     if (body->contents.text) fs_give ((void **) &body->contents.text);
  1755.     break;
  1756.   case TYPEMULTIPART:        /* multiple part */
  1757.     mail_free_body_part (&body->contents.part);
  1758.     break;
  1759.   case TYPEMESSAGE:        /* encapsulated message */
  1760.     mail_free_envelope (&body->contents.msg.env);
  1761.     mail_free_body (&body->contents.msg.body);
  1762.     if (body->contents.msg.text)
  1763.       fs_give ((void **) &body->contents.msg.text);
  1764.     break;
  1765.   case TYPEAPPLICATION:        /* application data */
  1766.   case TYPEAUDIO:        /* audio */
  1767.   case TYPEIMAGE:        /* static image */
  1768.   case TYPEVIDEO:        /* video */
  1769.     if (body->contents.binary) fs_give (&body->contents.binary);
  1770.     break;
  1771.   default:
  1772.     break;
  1773.   }
  1774. }
  1775.  
  1776. /* Mail garbage collect body parameter
  1777.  * Accepts: pointer to body parameter pointer
  1778.  */
  1779.  
  1780. void mail_free_body_parameter (parameter)
  1781.     PARAMETER **parameter;
  1782. {
  1783.   if (*parameter) {        /* only free if exists */
  1784.     if ((*parameter)->attribute) fs_give ((void **) &(*parameter)->attribute);
  1785.     if ((*parameter)->value) fs_give ((void **) &(*parameter)->value);
  1786.                 /* run down the list as necessary */
  1787.     mail_free_body_parameter (&(*parameter)->next);
  1788.                 /* return body part to free storage */
  1789.     fs_give ((void **) parameter);
  1790.   }
  1791. }
  1792.  
  1793.  
  1794. /* Mail garbage collect body part
  1795.  * Accepts: pointer to body part pointer
  1796.  */
  1797.  
  1798. void mail_free_body_part (part)
  1799.     PART **part;
  1800. {
  1801.   if (*part) {            /* only free if exists */
  1802.     mail_free_body_data (&(*part)->body);
  1803.                 /* run down the list as necessary */
  1804.     mail_free_body_part (&(*part)->next);
  1805.     fs_give ((void **) part);    /* return body part to free storage */
  1806.   }
  1807. }
  1808.  
  1809. /* Mail garbage collect message cache
  1810.  * Accepts: mail stream
  1811.  *
  1812.  * The message cache is set to NIL when this function finishes.
  1813.  */
  1814.  
  1815. void mail_free_cache (stream)
  1816.     MAILSTREAM *stream;
  1817. {
  1818.                 /* flush the cache */
  1819.   (*mailcache) (stream,(long) 0,CH_INIT);
  1820.   stream->msgno = 0;        /* free this cruft too */
  1821.   mail_free_envelope (&stream->env);
  1822.   mail_free_body (&stream->body);
  1823.   if (stream->text) fs_give ((void **) &stream->text);
  1824. }
  1825.  
  1826.  
  1827. /* Mail garbage collect cache element
  1828.  * Accepts: pointer to cache element pointer
  1829.  */
  1830.  
  1831. void mail_free_elt (elt)
  1832.     MESSAGECACHE **elt;
  1833. {
  1834.                 /* only free if exists and no sharers */
  1835.   if (*elt && !--(*elt)->lockcount) fs_give ((void **) elt);
  1836.   else *elt = NIL;        /* else simply drop pointer */
  1837. }
  1838.  
  1839.  
  1840. /* Mail garbage collect long cache element
  1841.  * Accepts: pointer to long cache element pointer
  1842.  */
  1843.  
  1844. void mail_free_lelt (lelt)
  1845.     LONGCACHE **lelt;
  1846. {
  1847.                 /* only free if exists and no sharers */
  1848.   if (*lelt && !--(*lelt)->elt.lockcount) {
  1849.     mail_free_envelope (&(*lelt)->env);
  1850.     mail_free_body (&(*lelt)->body);
  1851.     fs_give ((void **) lelt);    /* return cache element to free storage */
  1852.   }
  1853.   else *lelt = NIL;        /* else simply drop pointer */
  1854. }
  1855.  
  1856. /* Mail garbage collect envelope
  1857.  * Accepts: pointer to envelope pointer
  1858.  */
  1859.  
  1860. void mail_free_envelope (env)
  1861.     ENVELOPE **env;
  1862. {
  1863.   if (*env) {            /* only free if exists */
  1864.     if ((*env)->remail) fs_give ((void **) &(*env)->remail);
  1865.     mail_free_address (&(*env)->return_path);
  1866.     if ((*env)->date) fs_give ((void **) &(*env)->date);
  1867.     mail_free_address (&(*env)->from);
  1868.     mail_free_address (&(*env)->sender);
  1869.     mail_free_address (&(*env)->reply_to);
  1870.     if ((*env)->subject) fs_give ((void **) &(*env)->subject);
  1871.     mail_free_address (&(*env)->to);
  1872.     mail_free_address (&(*env)->cc);
  1873.     mail_free_address (&(*env)->bcc);
  1874.     if ((*env)->in_reply_to) fs_give ((void **) &(*env)->in_reply_to);
  1875.     if ((*env)->message_id) fs_give ((void **) &(*env)->message_id);
  1876.     if ((*env)->newsgroups) fs_give ((void **) &(*env)->newsgroups);
  1877.     if ((*env)->followup_to) fs_give ((void **) &(*env)->followup_to);
  1878.     if ((*env)->references) fs_give ((void **) &(*env)->references);
  1879.     fs_give ((void **) env);    /* return envelope to free storage */
  1880.   }
  1881. }
  1882.  
  1883.  
  1884. /* Mail garbage collect address
  1885.  * Accepts: pointer to address pointer
  1886.  */
  1887.  
  1888. void mail_free_address (address)
  1889.     ADDRESS **address;
  1890. {
  1891.   if (*address) {        /* only free if exists */
  1892.     if ((*address)->personal) fs_give ((void **) &(*address)->personal);
  1893.     if ((*address)->adl) fs_give ((void **) &(*address)->adl);
  1894.     if ((*address)->mailbox) fs_give ((void **) &(*address)->mailbox);
  1895.     if ((*address)->host) fs_give ((void **) &(*address)->host);
  1896.     if ((*address)->error) fs_give ((void **) &(*address)->error);
  1897.     mail_free_address (&(*address)->next);
  1898.     fs_give ((void **) address);/* return address to free storage */
  1899.   }
  1900. }
  1901.